package com.amkai.aes.webservice.binary.objecthandler.diee;

import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.amkai.aes.aescommon.diee.DIEEDetailedException;
import com.amkai.aes.aescommon.diee.DIEEException;
import com.amkai.aes.aescommon.service.diee.DIEECommonUtils;
import com.amkai.aes.aescommon.service.diee.DIEEGeneralJob;
import com.amkai.aes.aescommon.service.diee.DIEEGeneralResult;
import com.amkai.aes.aescommon.service.diee.remote.RemoteServerTask;
import com.amkai.aes.generatedbeans.binarymgr.BinaryData;
import com.amkai.aes.generatedbeans.diee.AcrDefinition;
import com.amkai.aes.generatedbeans.diee.FormPrintingKind;
import com.amkai.aes.generatedbeans.diee.PassportAssociation;
import com.amkai.aes.generatedbeans.diee.ReportDefinition;
import com.amkai.aes.generatedbeans.diee.ReportGroup;
import com.amkai.aes.generatedbeans.diee.ServerTask;
import com.amkai.aes.generatedbeans.diee.TaskXML;
import com.amkai.aes.generatedbeans.diee.TcpIpConfiguration;
import com.amkai.aes.generatedbeans.organization.Department;
import com.amkai.aes.generatedbeans.passport.Passport;
import com.amkai.aes.generatedbeans.registration.items.ExternalIdType;
import com.amkai.aes.server.diee.DIEEServerUtils;
import com.amkai.aes.server.diee.IReportGroupValidator;
import com.amkai.aes.server.diee.ReportInitializer;
import com.amkai.common.extensions.hibernate.config.manager.SessionManager;
import com.amkai.diee.DIEEFormatter;
import com.amkai.diee.entities.DIEEComplexEntity;
import com.amkai.diee.entities.DIEEProperty;
import com.amkai.diee.utils.DIEEDebugger;
import com.amkai.diee.utils.Utils;
import com.amkai.diee.utils.acr.ACRDefinitionJAXBManager;
import com.amkai.diee.utils.acr.generated.ACRDefinition;
import com.amkai.diee.utils.acr.generated.DataSource;
import com.amkai.diee.utils.collections.MultiMap;
import com.amkai.extensions.server.services.ITcpIpManager;
import com.amkai.extensions.server.services.ServerServiceManager;
import com.amkai.rad.designerbase.beans.ChangedKind;
import com.amkai.rad.designerbase.beans.RADBean;

public class DIEEGeneralHandler extends AbstractDIEEHandler<DIEEGeneralJob, DIEEGeneralResult> {

	private Comparator<Comparable> comparator = new Comparator<Comparable>() {
		@Override
		public int compare(Comparable o1, Comparable o2) {
			if (o1==null) return -1;
			if (o2==null) return 1;
			return (o1.compareTo(o2));
		}
	};
	private IReportGroupValidator taskXMLServerSideReportGroupValidator = new IReportGroupValidator() {
		@Override
		public boolean isValid(ReportGroup group) {
			if (hasTaskXMLServerSide(group)) {
				group.initDirectFields();
				return true;
			} else {
				return false;
			}
		}
	};

	@Override
	protected DIEEGeneralResult createResult() {
		return new DIEEGeneralResult();
	}
	
	@Override
	protected String getName() {
		return "DIEE GENERAL JOB";
	}
	
	@Override
	public void process(DIEEGeneralJob job, Serializable object, DIEEGeneralResult result, SessionManager sessionManager, DIEEDebugger debugger) throws Exception, Error {
		Department department = job.getModuleDepartment();
		if (department!=null) {
			department = sessionManager.get(Department.class, department.getId());
		}
		
		/* Query Report Groups For Server Tasks */
		if (job.getReportGroupSysIdForServerTasks()!=null) {
			String sysId = job.getReportGroupSysIdForServerTasks();
			info("Querying report groups for server tasks" + (sysId.isEmpty() ? "" : " (parent group: " + sysId + ')') + "...");
			List<ReportGroup> groups = null;
			synchronized (taskXMLServerSideReportGroupValidator) {
				groups = DIEEServerUtils.queryReportGroups(sessionManager, sysId.isEmpty() ? null : sysId, taskXMLServerSideReportGroupValidator);
			}
			if (groups!=null) {
				info("Count: "+groups.size());
				for (ReportGroup group : groups) DIEEServerUtils.initTaskXMLs(group, true, true);
			} else {
				info("Count: 0");
			}
			result.setReportGroups(groups);
		}
			
		/* Query Report Definitions For Form Printing */
		if (job.isQueryReportDefinitionsForFormPrinting()) {
			info("Querying report definitions for form printing...");
			FormPrintingKind formPrintingKind = job.getFormPrintingKind();
			if (formPrintingKind==null) {
				error("The form printing kind is null.");
			} else {
				info("The form printing kind: "+formPrintingKind.toString()+".");
				List<ReportGroup> groups = sessionManager.execute("from ReportGroup x where x.parent is null and x.formPrintingKindWrapper.id="+formPrintingKind.ordinal()+" and x.active=true order by x.name").list();
				if (groups!=null) {
					for (ReportGroup group : groups) {
						addActiveReportDefinitionsToList(group, result, department);
					}
				}
			}
		}
			
		/* Query Report Definitions By Sys ID */
		if (job.isQueryReportDefinitionsBySysIds()) {
			info("Querying report definitions by sys IDs...");
			List<String> sysIds = job.getReportDefinitionSysIds();
			if (sysIds==null) {
				error("Sys ID not found.");
			} else {
				List<ReportDefinition> defs = DIEEServerUtils.queryReportDefinitionsBySysId(sessionManager, department, sysIds.toArray(new String[sysIds.size()]));
				if (defs!=null) {
					for (ReportDefinition def : defs) {
						result.addReportDefinition(def);
					}
				}
			}
		}
			
		/* Query Report Definitions By ID */
		if (job.isQueryReportDefinitionsByIds()) {
			info("Querying report definitions by IDs...");
			List<Long> ids = job.getReportDefinitionIds();
			if (ids==null) {
				error("ID not found.");
			} else {
				List<ReportDefinition> defs = DIEEServerUtils.queryReportDefinitionsById(sessionManager, department, ids.toArray(new Long[ids.size()]));
				if (defs!=null) {
					for (ReportDefinition def : defs) {
						result.addReportDefinition(def);
					}
				}
			}
		}
			
		/* Query Report Definitions By Report Group Sys ID */
		if (job.isQueryReportDefinitionsByReportGroupSysIds()) {
			info("Querying report definitions by report group sys IDs...");
			List<String> sysIds = job.getReportGroupSysIds();
			if (sysIds==null) {
				error("Sys ID not found.");
			} else {
				List<ReportDefinition> defs = DIEEServerUtils.queryReportDefinitionsByGroupSysId(sessionManager, department, sysIds.toArray(new String[sysIds.size()]));
				if (defs!=null) {
					for (ReportDefinition def : defs) {
						result.addReportDefinition(def);
					}
				}
			}
		}
			
		/* Query Task XMLs By Sys ID */
		if (job.isQueryTaskXMLsBySysIds()) {
			info("Querying task xmls by sys IDs...");
			List<String> sysIds = job.getTaskXMLSysIds();
			if (sysIds==null) {
				error("Sys ID not found.");
			} else {
				for (String sysId : sysIds) {
					List<TaskXML> defs = (List<TaskXML>)sessionManager.execute("from TaskXML where sysId='"+sysId+"'").list();
					if (defs!=null) {
						for (TaskXML def : defs) {
							DIEEServerUtils.initTaskXML(def);
							result.addTaskXML(def);
						}
					}
				}
			}
		}
			
		/* Query Available Extractor Names */
		if (job.isQueryDataExtractorNames()) {
			info("Collect the data extractor names...");
			result.setDataExtractorNames(ReportInitializer.getInstance().getDataExtractorNames());
		}
			
		/* Query Available Response Checker Names */
		if (job.isQueryResponseCheckerNames()) {
			info("Collect the response checker names...");
			result.setResponseCheckerNames(ReportInitializer.getInstance().getResponseCheckerNames());
		}
			
		/* Query Property Values */
		if (job.isQueryPropertyValues()) {
			info("Querying the values for property '"+job.getPropertyNameForQueryingPropertyValues()+"'...");
			DIEEComplexEntity<RADBean> entity = getEntity((Class<RADBean>)job.getBeanClassForQueryingPropertyValues(), sessionManager);
			if (entity!=null) {
				info("	root type: "+entity.getType().getCanonicalName());
				entity.setArgumentValue("department", department);
				DIEEProperty<?,?> property = entity.getProperty(job.getPropertyNameForQueryingPropertyValues());
				if (property==null) {
					info("The property not found: "+job.getPropertyNameForQueryingPropertyValues()+"!");
				} else {
					DIEEProperty<?,?> active = entity.getProperty("active");
					if (active==null) active = entity.getProperty("isActive");
					if (active!=null && !active.getType().equals(Boolean.class))
						active = null;
					boolean queryable = property.isEnabledInQueryAll();
					List list = null;
					if (queryable) {
						info("	querying directly from the database...");
						String qstr = "select distinct x." +
							job.getPropertyNameForQueryingPropertyValues() +
							" from " + job.getBeanClassForQueryingPropertyValues().getCanonicalName() +
							" as x where x." + job.getPropertyNameForQueryingPropertyValues() + " is not null";
						if (active!=null)
							qstr += " and x." + active.getOriginalName() + "=true";
						qstr += " order by x."+job.getPropertyNameForQueryingPropertyValues();
						info("	query string:");
						info("		"+qstr);
						list = sessionManager.execute(qstr).list();
					} else {
						info("	collecting the values...");
						list = new ArrayList<Object>();
						String qstr = "from "+job.getBeanClassForQueryingPropertyValues().getCanonicalName();
						List<RADBean> beans = sessionManager.execute(qstr).list();
						int cycles = 0;
						for (RADBean bean : beans) {
							if (cycles>Utils.MEMORY_CHECK_LIMIT) {
								Utils.checkMemoryUsage();
								cycles = 0;
							}
							entity.setValue(bean);
							if (active==null || Boolean.TRUE.equals(active.getValue())) {
								Object value = property.getValue();
								if (value!=null && !list.contains(value)) list.add(value);
							}
							cycles++;
						}
						if (Comparable.class.isAssignableFrom(property.getType())) {
							synchronized (comparator) {
								Collections.sort((List<Comparable<?>>)list, comparator);
							}
						}
					}
					info("	record count: "+list.size());
					result.setPropertyValues(list);
				}
			}
		} else {
			String hqlQuery = job.getHqlQuery();
			if (hqlQuery!=null) {
				info("Querying the database...");
				info("	query string:");
				info("		"+hqlQuery);
				List list = sessionManager.execute(hqlQuery).list();
				info("	record count: "+(list==null ? "0": list.size()));
				result.setPropertyValues(list);
			}
		}
			
		/* Initialize Properties */
		if (job.isInitializeProperties()) {
			info("Initializing the beans...");
			DIEEComplexEntity<RADBean> entity = getEntity((Class<RADBean>)job.getBeanClassForInitialize(), sessionManager);
			if (entity!=null) {
				try {
					entity.setArgumentValue("department", department);
					ArrayList<String> props = new ArrayList<String>();
					ArrayList<String> formats = new ArrayList<String>();
					for (String name : job.getPropertyNamesForInitialize()) {
						DIEEProperty<?,?> prop = entity.getProperty(name);
						if (prop!=null) {
							name = prop.getFullChildName();
							props.add(name);
							info("	Property: "+name+".");
						} else {
							// is this a format string?
							formats.add(name);
							warn("	The property not found: "+name+" - maybe it is a format string.");
						}
					}
					String qstr = "from " + entity.getType().getCanonicalName() + " as x where x.id in (:ids)";
					List<RADBean> beans = sessionManager.execute(qstr).setParameterList("ids", job.getBeanIdsForInitialize()).list();
					DIEEFormatter formatter = null;
					if (!formats.isEmpty()) {
						formatter = new DIEEFormatter();
						formatter.setSessionManager(sessionManager);
						formatter.setDepartment(department);
					}
					int cycles = 0;
					for (RADBean bean : beans) {
						if (cycles>Utils.MEMORY_CHECK_LIMIT) {
							Utils.checkMemoryUsage();
							cycles = 0;
						}
						entity.setValue(bean);
						for (String prop : props) {
							entity.initProperty(prop);
						}
						for (String format : formats) {
							formatter.format((Class<RADBean>)bean.getClass(), format, bean);
						}
						cycles++;
					}
					result.setBeans(beans);
				} catch (Exception ex) {
					throw ex;
				} catch (Error err) {
					throw err;
				}
			}
		}
			
		/* Handle Server Task */
		List<RemoteServerTask> remoteServerTasks = job.getRemoteServerTasks();
		if (remoteServerTasks!=null && remoteServerTasks.size()>0) {
			info("Handle remote server tasks: count = "+Integer.toString(remoteServerTasks.size())+"...");
			sessionManager.openTransaction();
			for (RemoteServerTask remoteServerTask : remoteServerTasks) {
				ServerTask task = handleServerTask(remoteServerTask, department, sessionManager);
				if (task!=null)
					result.setServerTask(DIEEServerUtils.initServerTask(task));
			}
			sessionManager.commitTransaction();
			sessionManager.closeSession();
			sessionManager.openSession();
		}
		
		/* Query Server Tasks */
		if (job.isQueryServerTasks()) {
			info("Querying server-side tasks...");
			List<ServerTask> tasks = sessionManager.execute(
					"select t from ServerTask as t " +
					"left join fetch t.taskXML as x " +
					"left join fetch x.reportGroup as g " +
					"where t.department.id = " + department.getId()
				).list();
			result.addServerTask(null);
			for (ServerTask task : tasks) {
				result.addServerTask(DIEEServerUtils.initServerTask(task));
			}
		}
		/* Server Task Additional Info */
		Long idForServerTaskInfo = job.getIdForServerTaskInfo();
		if (idForServerTaskInfo!=null) {
			ServerTask task = sessionManager.get(ServerTask.class, idForServerTaskInfo);
			if (task!=null) {
				StringBuilder str = new StringBuilder();
				str.append("<html>");
				// name
				str.append("<b>Action</b>: ");
				str.append(task.getName());
				str.append("<br>");
				// messages in the database
				int cnt = ((Number)sessionManager.execute("select count(*) from MessageToProvide as m where m.serverTask.id = " + idForServerTaskInfo).uniqueResult()).intValue();
				if (cnt>0) {
					str.append("<b>Messages in the message queue</b>: ");
					str.append(cnt);
					str.append(".<br>");
				} else {
					str.append("No messages in the message queue.<br>");
				}
				str.append("</html>");
				result.setHtmlInformation(str.toString());
			}
		}
		
		/* Query ACR definitions */
		if (job.isQueryACR()) {
			info("Querying ACR definitions...");
			String qstr = "select x from AcrDefinition as x ";
			if (department!=null) {
				qstr +=
					"left join x.departmentKindAssociations as a, " +
					"Department as d " +
					"left join d.departmentType as t " +
					"where t.kindWrapper.id = a.departmentKindWrapper.id and d.id = " + department.getId()
				;
			}
			List<AcrDefinition> defs = sessionManager.execute(qstr).list();
			ACRDefinitionJAXBManager manager = new ACRDefinitionJAXBManager();
			ACRDefinition acr = new ACRDefinition();
			if (defs!=null) {
				for (AcrDefinition def : defs) {
					info("ACR definition #" + def.getId());
					String str = DIEEServerUtils.getStringData(def.getBinaryData());
					if (str!=null) {
						manager.unmarshal(str);
						ACRDefinition acrDef = manager.getDocument();
						if (acrDef!=null) acr.getDataSource().addAll(acrDef.getDataSource());
					}
				}
				Collections.sort(acr.getDataSource(), new Comparator<DataSource>() {
					@Override
					public int compare(DataSource o1, DataSource o2) {
						String n1 = o1.getName();
						if (n1==null) return -1;
						String n2 = o2.getName();
						if (n2==null) return 1;
						return n1.compareTo(n2);
					}
				});
			}
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			manager.writeToOutputStream(out, acr);
			result.setAcrData(out.toString());
		}
		
		/* Save TCP/IP configurations */
		if (job.getTcpIpConfigurations()!=null || job.getStartTcpIpListenerConfiguration()!=null) {
			info("Saving TCP/IP configurations...");
			sessionManager.openTransaction();
			MultiMap<Integer, Long> portMap = new MultiMap<Integer, Long>();
			ArrayList<TcpIpConfiguration> toStartup = new ArrayList<TcpIpConfiguration>();
			ITcpIpManager manager = ServerServiceManager.getInstance().getTcpIpManager();
			try {
				// check port (should be unique)
				List<Object[]> ports = sessionManager.execute("select port, id from TcpIpConfiguration").list();
				if (ports!=null) {
					for (Object[] objs : ports) {
						portMap.putValue((Integer)objs[0], (Long)objs[1]);
					}
				}
				// save beans
				if (job.getTcpIpConfigurations()!=null)
					for (TcpIpConfiguration cfg : job.getTcpIpConfigurations())
						handleTcpIpConfiguration(cfg, sessionManager, department, portMap, toStartup, false);
				if (job.getStartTcpIpListenerConfiguration()!=null)
					handleTcpIpConfiguration(job.getStartTcpIpListenerConfiguration(), sessionManager, department, portMap, toStartup, true);
				sessionManager.commitTransaction();
			} catch (Exception ex) {
				sessionManager.rollbackTransaction();
				throw ex;
			}
			// start listeners
			for (TcpIpConfiguration cfg : toStartup) {
				try {
					manager.startupListener(cfg);
				} catch (Exception ex) {
					if (portMap.size(cfg.getPort())>1)
						manager.setException(cfg, new DIEEException("Port is already in use: " + cfg.getPort()));
					else
						manager.setException(cfg, ex);
				}
			}
		}
		
		/* Stop TCP/IP listener */
		if (job.getStopTcpIpListenerConfiguration()!=null) {
			ServerServiceManager.getInstance().getTcpIpManager().shutdownListener(job.getStopTcpIpListenerConfiguration());
			TcpIpConfiguration cfg = sessionManager.get(TcpIpConfiguration.class, job.getStopTcpIpListenerConfiguration().getId());
			init(cfg);
			result.addTcpIpConfiguration(cfg);
		}
		
		/* Start TCP/IP listener */
		if (job.getStartTcpIpListenerConfiguration()!=null) {
			TcpIpConfiguration cfg = sessionManager.get(TcpIpConfiguration.class, job.getStartTcpIpListenerConfiguration().getId());
			init(cfg);
			result.addTcpIpConfiguration(cfg);
		}
		
		/* Query TCP/IP configurations */
		if (job.isQueryTcpIpConfigurations() || job.getTcpIpConfigurations()!=null) {
			info("Querying TCP/IP configurations...");
			String qstr = 
				"select t from TcpIpConfiguration as t " +
				"left join fetch t.taskXML as x " +
				"left join fetch t.externalIdType";
			if (department!=null)
				qstr += " where t.defaultDepartment.id = " + department.getId();
			qstr += " order by t.name";
			List<TcpIpConfiguration> list = sessionManager.execute(qstr).list();
			if (list!=null) {
				info("Count: " + list.size());
				result.addTcpIpConfigurations(list);
				for (TcpIpConfiguration conf : list)
					init(conf);
			} else {
				info("Count: 0");
			}
		}
		
		/* Load Binary Data object */
		if (job.getTargetBeanClassForBinary()!=null && job.getTargetBeanIdForBinary()!=null && job.getTargetBeanPropertyNameForBinary()!=null) {
			// AES-16118
			info("Load binary data for " + job.getTargetBeanClassForBinary().getSimpleName() + "(" + job.getTargetBeanIdForBinary() + ")." + job.getTargetBeanPropertyNameForBinary() + "...");
			Object obj = sessionManager.execute(
					"select x." + job.getTargetBeanPropertyNameForBinary() +
					" from " + job.getTargetBeanClassForBinary().getSimpleName() + " as x" +
					" where x.id = " + job.getTargetBeanIdForBinary()
				).uniqueResult();
			if (obj!=null) {
				if (obj instanceof BinaryData) {
					BinaryData binary = (BinaryData)obj;
					binary.getOriginalName();
					binary.getStoreName();
					result.setBinaryData(binary);
					info(" - found");
				} else {
					error(" - not a BinaryData instance: " + obj);
				}
			} else {
				info(" - not found");
			}
		}
	}
	
	private final void handleTcpIpConfiguration(
			TcpIpConfiguration cfg, 
			SessionManager sessionManager, 
			Department department, 
			MultiMap<Integer, Long> portMap, List<TcpIpConfiguration> toStartup,
			boolean autoStart) throws Exception {
		ITcpIpManager manager = ServerServiceManager.getInstance().getTcpIpManager();
		if (cfg.getId()!=null) {
			if (cfg.getChangedKind()!=ChangedKind.DELETED) {
				TcpIpConfiguration newCfg = sessionManager.get(TcpIpConfiguration.class, cfg.getId());
				int oldPort = newCfg.getPort();
				// AES-14589
				boolean changed = (
						cfg.getPort()!=oldPort ||
						(!DIEECommonUtils.equals(cfg.getInputKind(), newCfg.getInputKind())) ||
						(!DIEECommonUtils.equals(cfg.getTaskXML(), newCfg.getTaskXML())) ||
						(!DIEECommonUtils.equals(cfg.getExternalIdType(), newCfg.getExternalIdType()))
					);
				newCfg.setName(cfg.getName());
				newCfg.setPort(cfg.getPort());
				newCfg.setInputKind(cfg.getInputKind());
				if (department!=null) newCfg.setDefaultDepartment(department);
				if (cfg.getTaskXML()==null) {
					newCfg.setTaskXML(null);
				} else {
					newCfg.setTaskXML(sessionManager.get(TaskXML.class, cfg.getTaskXML().getId()));
					newCfg.getTaskXML().getBinaryData().getStoreName();
				}
				if (cfg.getExternalIdType()==null)
					newCfg.setExternalIdType(null);
				else
					newCfg.setExternalIdType(sessionManager.get(ExternalIdType.class, cfg.getExternalIdType().getId()));
				cfg = newCfg;
				sessionManager.saveRAD(cfg);
				info(" updated: #" +cfg.getId() + " " + cfg.getName());
				if (changed) {
					manager.shutdownListener(cfg);
					portMap.remove(oldPort, cfg.getId());
					portMap.putValue(cfg.getPort(), cfg.getId());
				}
				// AES-16451
				if (autoStart)
					toStartup.add(cfg);
			} else {
				sessionManager.saveRAD(cfg);
				info(" deleted: #" +cfg.getId() + " " + cfg.getName());
				for (Integer port : portMap.keySet())
					portMap.remove(port, cfg.getId());
				manager.shutdownListener(cfg);
			}
		} else if (cfg.getChangedKind()!=ChangedKind.DELETED) {
			cfg.setChangedKind(ChangedKind.CREATED);
			if (department!=null) cfg.setDefaultDepartment(department);
			if (cfg.getTaskXML()!=null) {
				cfg.setTaskXML(sessionManager.get(TaskXML.class, cfg.getTaskXML().getId()));
				cfg.getTaskXML().getBinaryData().getStoreName();
			}
			sessionManager.saveRAD(cfg);
			info(" inserted: #" +cfg.getId() + " " + cfg.getName());
			portMap.putValue(cfg.getPort(), cfg.getId());
			toStartup.add(cfg);
		}
	}
	
	private final void init(TcpIpConfiguration conf) {
		conf.getTaskXML().getName();
		if (conf.getTaskXML().getBinaryData()!=null)
			conf.getTaskXML().getBinaryData().getStoreName();
		if (conf.getExternalIdType()!=null)
			conf.getExternalIdType().getName();
		Utils.setCachedValue(conf, "started", ServerServiceManager.getInstance().getTcpIpManager().isStarted(conf));
		Utils.setCachedValue(conf, "exception", ServerServiceManager.getInstance().getTcpIpManager().getException(conf));
	}
	
	private void addActiveReportDefinitionsToList(ReportGroup group, DIEEGeneralResult result, Department department) {
		if (group.isActive()) {
			Set<ReportDefinition> defs = group.getReportDefinitions();
			if (defs!=null) {
				for (ReportDefinition def : defs) {
					if (Utils.isEnabledForDepartment(department, def.getDepartmentAssociations(), def.getDepartmentKindAssociations(), def.getStateAssociations()))
						result.addReportDefinition(DIEEServerUtils.initReportDefinition(def));
				}
			}
			for (ReportGroup g : group.getChildren()) {
				addActiveReportDefinitionsToList(g, result, department);
			}
		}
	}

	private boolean hasTaskXMLServerSide(ReportGroup group) {
		Set<TaskXML> xmls = group.getTaskXMLs();
		if (xmls!=null && xmls.size()>0) {
			for (TaskXML xml : xmls) {
				if (Utils.isValid(xml) && xml.isServerSide()) return true;
			}
		}
		Set<ReportGroup> groups = group.getChildren();
		if (groups!=null && groups.size()>0) {
			for (ReportGroup g : groups) if (hasTaskXMLServerSide(g)) return true;
		}
		return false;
	}

	private final ServerTask handleServerTask(RemoteServerTask remoteServerTask, Department department, SessionManager sessionManager) throws Exception {
		Long id = remoteServerTask.getId();
		boolean removeTask = remoteServerTask.isRemove();
		if (id==null) {
			info("Register a new server task...");
		} else if (removeTask) {
			info("Remove server task #"+id.toString()+"...");
		} else {
			info("Register server task #"+id.toString()+"...");
		}
		Long xmlId = remoteServerTask.getTaskXMLId();
		ServerTask task;
		if (id==null) {
			task = new ServerTask();
		} else {
			task = sessionManager.get(ServerTask.class, id);
		}
		Set<PassportAssociation> mAssocs = task.getMessageReceiverAssociations();
		if (removeTask) {
			// AES-12870 - check the messages in the db
			Number n = (Number)sessionManager.execute(
					"select count(*) from MessageToProvide as m where m.serverTask.id = " + task.getId()
				).uniqueResult();
			if (n!=null && n.intValue()>0) {
				warn("Server task has messages to provide (count: " + n + ") - cannot be removed.");
				String m = (n.intValue()>1 ? "messages" : "message");
				throw new DIEEDetailedException(
						"Action has " + (n.intValue()>1 ? n.toString() + ' ' : "") + m + " in the database - you can't remove it.",
						"Hints:\n" +
						" 1. try to remove the action later when the messages are posted\n" +
						" 2. - set the transport kind to 'save the data into a file'\n" +
						"     - set the file name and try to remove the action later\n" +
						"     - " + m + " will be ignored"
					);
			} else {
				// AES-13542
				if (mAssocs!=null)
					for (PassportAssociation assoc : mAssocs) sessionManager.delete(assoc);
				sessionManager.delete(task);
			}
			return null;
		} else {
			task.setName(remoteServerTask.getName());
			task.setEventKind(remoteServerTask.getEventKind());
			task.setTransportKind(remoteServerTask.getTransportKind());
			task.setFileName(remoteServerTask.getFileName());
			task.setServerIP(remoteServerTask.getServerIP());
			task.setServerPort(remoteServerTask.getServerPort());
			task.setReceiveServerPort(remoteServerTask.getReceiveServerPort());
			task.setRetryLimit(remoteServerTask.getRetryLimit());
			task.setResultCheckerClass(remoteServerTask.getResultCheckerClass());
			task.setUrl(remoteServerTask.getUrl());
			// AES-17722
			task.setUsername(remoteServerTask.getUserName());
			task.setPassword(remoteServerTask.getPassword());
			task.setErrorMessage(remoteServerTask.isErrorMesssage());
			task.setSuccessMessage(remoteServerTask.isSuccessMesssage());
			task.setTaskXML(sessionManager.get(TaskXML.class, xmlId));
			// AES-14823
			task.setDepartment(department);
			// AES-12392
			Long[] passportIds = remoteServerTask.getPassportIds();
			HashSet<PassportAssociation> passportAssocs = new HashSet(task.getMessageReceiverAssociations());
			HashSet<PassportAssociation> newPassportAssocSet = new HashSet<PassportAssociation>();
			if (passportIds!=null) {
				for (Long passportId : passportIds) {
					Passport passport = sessionManager.get(Passport.class, passportId);
					if (passport!=null) {
						PassportAssociation found = null;
						for (PassportAssociation pa : passportAssocs) {
							if (passport.equals(pa.getPassport())) {
								found = pa;
								break;
							}
						}
						if (found==null) {
							found = new PassportAssociation();
							found.setPassport(passport);
							found.setServerTask(task);
							sessionManager.save(found);
						} else {
							passportAssocs.remove(found);
						}
						newPassportAssocSet.add(found);
					}
				}
			}
			for (PassportAssociation pa : passportAssocs) {
				sessionManager.delete(pa);
			}
			task.getMessageReceiverAssociations().clear();
			task.getMessageReceiverAssociations().addAll(newPassportAssocSet);
			if (id==null)
				sessionManager.save(task);
			else
				sessionManager.update(task);
			return task;
		}
	}
}
